// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <time.h>
#include <math.h>
#include "mlink.h"
#include "mlink_trigger.h"
#include "mlink_subdevice.h"

/*********************/

/********************/
#ifdef CONFIG_MLINK_TRIGGER_LIST_MAX_NUM
#define MLINK_TRIGGER_LIST_MAX_NUM CONFIG_MLINK_TRIGGER_LIST_MAX_NUM
#else
#define MLINK_TRIGGER_LIST_MAX_NUM 32
#endif
#define MLINK_TRIGGER_STORE_KEY    "MLINK_TRIGGER"

/**
 * @brief Used to compare whether the trigger condition is met
 */
typedef struct trigger_compare {
    union {
        struct {
            uint32_t equal        : 1;
            uint32_t unequal      : 1;
            uint32_t greater_than : 1;
            uint32_t less_than    : 1;
            uint32_t variation    : 1;
            uint32_t rising       : 1;
            uint32_t falling      : 1;
        };
        uint32_t val;
    } flag;

    float value;
    float equal;
    float unequal;
    float greater_than;
    float less_than;
    float variation;
    float rising;
    float falling;
} trigger_compare_t;

typedef struct trigger_valid_time {
	time_t start_date;
	time_t end_date;
	uint32_t start_time;
	uint32_t end_time;
	uint8_t week;
}trigger_valid_time_t;

/**
 * @brief Type of trigger function
 */
enum trigger_type {
    TRIGGER_LINKAGE,
    TRIGGER_SYNC,
    TRIGGER_TIMING,
    TRIGGER_DELAY,
    TRIGGER_LOOP,
	TRIGGER_AND,
	TRIGGER_OR,
};

/**
 * @brief List of trigger functions
 */
typedef struct mlink_trigger {
    char name[16];
	char parent[16];
	char *alias;
	bool enable;
	char node[4];
    char trigger_cid[32];
    uint8_t trigger_type;
    int trigger_params[8];
    uint32_t communicate_type;
    trigger_compare_t trigger_compare;
	uint16_t condition_num;
	char **condition_list;
    uint16_t raw_data_size;
    uint16_t addrs_num;
    uint8_t *addrs_list;
	int content_num;
    char **execute_content;
	trigger_valid_time_t valid_time;
	time_t last_trigger_time;
    struct mlink_trigger *next;
} mlink_trigger_t;

/**
 * @brief Store trigger function
 */
typedef struct {
    uint16_t size;
    char name[16];
} mlink_trigger_store_t;

static const char *TAG                 = "mlink_trigger";
static mlink_trigger_t *g_trigger_list = NULL;
extern mlink_characteristic_func_t mlink_device_get_value;

static mdf_err_t execute_trigger_content(mlink_trigger_t *trigger_idex);
static mdf_err_t mlink_trigger_multi_condition_handle(mlink_trigger_t *trigger_idex);

static void trigger_free(mlink_trigger_t **item)
{
	if(*item!= NULL){
		MDF_FREE((*item)->alias);
        MDF_FREE((*item)->addrs_list);
		for(int i=0;i<(*item)->content_num;i++){
			MDF_FREE((*item)->execute_content[i]);
		}
		MDF_FREE((*item)->execute_content);
		for(int i=0;i<(*item)->condition_num;i++){
			MDF_FREE((*item)->condition_list[i]);
		}
		MDF_FREE((*item)->condition_list);
        MDF_FREE((*item));
	}

}
/*
	return time second
	time_str string formt "2023-5-5"
*/
static time_t date_str_to_sec(const char *time_str)
{
	struct tm info;
	if(3!=sscanf(time_str, "%d-%d-%d", &info.tm_year, &info.tm_mon, &info.tm_mday)){
		MDF_LOGW("time format error");
		return -1;
	}
	MDF_LOGD("time %d-%d-%d", info.tm_year, info.tm_mon, info.tm_mday);
	info.tm_year = info.tm_year - 1900;
	info.tm_mon = info.tm_mon - 1;
	info.tm_hour =0;
	info.tm_min =0;
	info.tm_sec = 0;
	info.tm_isdst = -1;
	time_t sec = mktime(&info);
	return sec;
}

/*
	return time second
	time_str string formt "08:00"
*/
static uint32_t time_str_to_sec(const char *time_str)
{
	int h,m;
	if(2!=sscanf(time_str, "%d:%d", &h, &m)){
		MDF_LOGW("time format error");
		return -1;
	}

	return (h*3600+m*60);
}

/*
	conver string to hour and mintue
	time_str in string formt "08:00"
	hour out
	min  out
*/
static void time_str_to_int(const char *time_str, uint8_t *hour, uint8_t *min)
{
	*hour = (time_str[0] - '0')*10 + (time_str[1] - '0');
	*min = (time_str[3] - '0')*10 + (time_str[4] - '0');
}

static mlink_trigger_t *mlink_trigger_parse(const char *raw_data)
{
    mdf_err_t ret                      = MDF_OK;
    char request_str[16]               = {0};
    char communicate_str[16]           = {0};
    char *trigger_content_str          = NULL;
    char *trigger_compare_str          = NULL;
	char *valid_time_str               = NULL;
    char **addrs_list_str              = NULL;
    mlink_trigger_t *trigger_item      = MDF_CALLOC(1, sizeof(mlink_trigger_t));
    trigger_compare_t *trigger_compare = &trigger_item->trigger_compare;

    memset(trigger_item, 0, sizeof(trigger_compare_t));

    trigger_item->raw_data_size = strlen(raw_data) + 1;

    ret = mlink_json_parse(raw_data, "name", trigger_item->name);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

	ret = mlink_json_parse(raw_data, "parent", trigger_item->parent); // 父节点名称
	if(ret != 0)
		trigger_item->parent[0] = 0;
	ret = mlink_json_parse(raw_data, "alias", &trigger_item->alias);// 别名
	ret = mlink_json_parse(raw_data, "node", trigger_item->node); // 节点
	if(ret != 0)
		trigger_item->node[0] = 0;
    ret = mlink_json_parse(raw_data, "enable", &trigger_item->enable);
	if(ret != 0)
		trigger_item->enable = true;

    ret = mlink_json_parse(raw_data, "trigger_cid", trigger_item->trigger_cid);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

    MDF_LOGD("name: %s, cid: \"%s\"", trigger_item->name, trigger_item->trigger_cid);

    ret = mlink_json_parse(raw_data, "execute_mac", &trigger_item->addrs_num);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

    addrs_list_str = MDF_CALLOC(trigger_item->addrs_num, sizeof(char *));
    trigger_item->addrs_list = MDF_CALLOC(trigger_item->addrs_num, 6);
    ret = mlink_json_parse(raw_data, "execute_mac", addrs_list_str);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

    for (int i = 0; i < trigger_item->addrs_num; ++i) {
        mlink_mac_str2hex(addrs_list_str[i], trigger_item->addrs_list + 6 * i);
        MDF_LOGV("str: %s, addrs_list: " MACSTR, addrs_list_str[i], MAC2STR(trigger_item->addrs_list + 6 * i));
        MDF_FREE(addrs_list_str[i]);
    }

    MDF_FREE(addrs_list_str);

    ret = mlink_json_parse(raw_data, "trigger_compare", &trigger_compare_str);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

    trigger_compare->flag.equal        = (mlink_json_parse(trigger_compare_str, "==", &trigger_compare->equal) == ESP_OK) ? true : false;
    trigger_compare->flag.unequal      = (mlink_json_parse(trigger_compare_str, "!=", &trigger_compare->unequal) == ESP_OK) ? true : false;
    trigger_compare->flag.greater_than = (mlink_json_parse(trigger_compare_str, ">",  &trigger_compare->greater_than) == ESP_OK) ? true : false;
    trigger_compare->flag.less_than    = (mlink_json_parse(trigger_compare_str, "<",  &trigger_compare->less_than) == ESP_OK) ? true : false;
    trigger_compare->flag.variation    = (mlink_json_parse(trigger_compare_str, "~",  &trigger_compare->variation) == ESP_OK) ? true : false;
    trigger_compare->flag.rising       = (mlink_json_parse(trigger_compare_str, "/",  &trigger_compare->rising) == ESP_OK) ? true : false;
    trigger_compare->flag.falling      = (mlink_json_parse(trigger_compare_str, "\\", &trigger_compare->falling) == ESP_OK) ? true : false;

    trigger_compare->value = -1;

    MDF_FREE(trigger_compare_str);

    ret = mlink_json_parse(raw_data, "trigger_content", &trigger_content_str);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

    ret = mlink_json_parse(trigger_content_str, "request", request_str);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

    if (!strcasecmp(request_str, "sync")) {
        trigger_item->trigger_type = TRIGGER_SYNC;
        ret = mlink_json_parse(trigger_content_str, "execute_cid", (char *)trigger_item->trigger_params);
        MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
    } else {

		if (!strcasecmp(request_str, "linkage")) {
			trigger_item->trigger_type = TRIGGER_LINKAGE;

		} else if (!strcasecmp(request_str, "timing")) {
			trigger_item->trigger_type = TRIGGER_TIMING;
			char time_str[8];
			ret = mlink_json_parse(trigger_content_str, "time", time_str);
			MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
			time_str_to_int(time_str, (uint8_t*)&trigger_item->trigger_params[0], (uint8_t*)&trigger_item->trigger_params[1]);

		} else if (!strcasecmp(request_str, "loop")) {
			trigger_item->trigger_type = TRIGGER_LOOP;
			char time_str[8];
			ret = mlink_json_parse(trigger_content_str, "time", time_str);
			MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
			time_str_to_int(time_str, (uint8_t*)&trigger_item->trigger_params[0], (uint8_t*)&trigger_item->trigger_params[1]);
		} 
		else if (!strcasecmp(request_str, "and") || !strcasecmp(request_str, "or")) {
			if(!strcasecmp(request_str, "and")) {
				trigger_item->trigger_type = TRIGGER_AND;
			}else if(!strcasecmp(request_str, "or")){
				trigger_item->trigger_type = TRIGGER_OR;
			}
			ret = mlink_json_parse(trigger_content_str, "condition", &trigger_item->condition_num);
			MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
			if(trigger_item->condition_num > 0) {
				trigger_item->condition_list = MDF_CALLOC(trigger_item->condition_num, sizeof(char *));
				ret = mlink_json_parse(trigger_content_str, "condition", trigger_item->condition_list);
				MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
			}
		}
		else {
			ret = ESP_FAIL;
			MDF_LOGW("no support request: %s", request_str);
			goto EXIT;
		}

		ret = mlink_json_parse(raw_data, "execute_content", &trigger_item->content_num);
		MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
		if(trigger_item->content_num > 0) {
			trigger_item->execute_content = MDF_CALLOC(trigger_item->content_num, sizeof(char *));
			ret = mlink_json_parse(raw_data, "execute_content", trigger_item->execute_content);
			MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
		}
	}

    if (mlink_json_parse(raw_data, "communicate_type", communicate_str) == MDF_OK) {
        if (!strcasecmp(communicate_str, "group")) {
            trigger_item->communicate_type = MLINK_ESPNOW_COMMUNICATE_GROUP;
        }
    }

    if(mlink_json_parse(raw_data, "valid_time", &valid_time_str) == MDF_OK){
		char time_str[20];
		int week;
	
		ret = mlink_json_parse(valid_time_str, "st", time_str);
		MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
		trigger_item->valid_time.start_time = time_str_to_sec(time_str);

		ret = mlink_json_parse(valid_time_str, "et", time_str);
		MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
		trigger_item->valid_time.end_time = time_str_to_sec(time_str);

		ret = mlink_json_parse(valid_time_str, "sd", time_str);
		MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
		trigger_item->valid_time.start_date = date_str_to_sec(time_str);

		ret = mlink_json_parse(valid_time_str, "ed", time_str);
		MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
		trigger_item->valid_time.end_date = date_str_to_sec(time_str);

		ret = mlink_json_parse(valid_time_str, "w", &week);
		MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");
		trigger_item->valid_time.week = (uint8_t)week&0xFF;

	}
    
EXIT:

    if (ret != MDF_OK) {
		trigger_free(&trigger_item);
        // MDF_FREE(trigger_item->addrs_list);
		// for(int i=0;i<trigger_item->content_num;i++){
		// 	MDF_FREE(trigger_item->execute_content[i]);
		// }
		// MDF_FREE(trigger_item->execute_content);
		// for(int i=0;i<trigger_item->condition_num;i++){
		// 	MDF_FREE(trigger_item->condition_list[i]);
		// }
		// MDF_FREE(trigger_item->condition_list);
        // MDF_FREE(trigger_item);
    }

    MDF_FREE(trigger_content_str);
    MDF_FREE(trigger_compare_str);
	MDF_FREE(valid_time_str);
    MDF_FREE(addrs_list_str);

    return (ret == MDF_OK) ? trigger_item : NULL;
}

mdf_err_t mlink_trigger_store_update()
{
    mdf_err_t ret   = MDF_OK;
    int trigger_num = 0;
    mlink_trigger_store_t *trigger_store = MDF_CALLOC(MLINK_TRIGGER_LIST_MAX_NUM, sizeof(mlink_trigger_store_t));

    for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next, trigger_num++) {
        strncpy(trigger_store[trigger_num].name, trigger_idex->name, sizeof(trigger_store[trigger_num].name));
        trigger_store[trigger_num].size = trigger_idex->raw_data_size;
    }

    if (trigger_num > 0) {
        ret = mdf_info_save(MLINK_TRIGGER_STORE_KEY, trigger_store, trigger_num * sizeof(mlink_trigger_store_t));
    } else {
        ret = mdf_info_erase(MLINK_TRIGGER_STORE_KEY);
    }

    MDF_FREE(trigger_store);
    MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "mdf_info_save, ret: %d", ret);

    return MDF_OK;
}

static mdf_err_t mlink_handle_get_trigger(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret      = ESP_OK;
    char *trigger_json = NULL;
    char *raw_data     = NULL;
	int count=0;
	int index_start=0, index_end=MLINK_TRIGGER_LIST_MAX_NUM;
	int limit[2];
	
	ret = mlink_json_parse(handle_data->req_data, "limit", limit);
	if(ret == MDF_OK){
		MDF_ERROR_GOTO(limit[0] < 0 || limit[0] >= MLINK_TRIGGER_LIST_MAX_NUM, EXIT, "limit arg error");
		MDF_ERROR_GOTO(limit[1] < 0, EXIT, "limit arg error");
	
		index_start = limit[0];
		limit[1] += index_start;
		index_end = limit[1] < MLINK_TRIGGER_LIST_MAX_NUM ? limit[1] : MLINK_TRIGGER_LIST_MAX_NUM;
		MDF_LOGI("index %d %d", index_start,index_end);
	}

	count=0;
    for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {
		if( (count >= index_start) && (count < index_end)){
			raw_data = MDF_MALLOC(trigger_idex->raw_data_size);
			ret = __mdf_info_load(trigger_idex->name, raw_data, trigger_idex->raw_data_size, LENGTH_TYPE_NUMBER);
			MDF_ERROR_GOTO(ret != MDF_OK, EXIT, " Load the information");
			
			mlink_json_pack(&trigger_json, "[]", raw_data);
			MDF_FREE(raw_data);
		}
		count++;
    }

    if (!trigger_json) {
        return MDF_OK;
    }

    handle_data->resp_size = mlink_json_pack(&handle_data->resp_data, "trigger", trigger_json);

EXIT:
    MDF_FREE(raw_data);
    MDF_FREE(trigger_json);
    return ret;
}

mdf_err_t mlink_trigger_add(const char *trigger_raw_data)
{
    MDF_PARAM_CHECK(trigger_raw_data);
    MDF_ERROR_CHECK(!g_trigger_list, MDF_ERR_NOT_INIT, "mlink_trigger is not initialized");

    mdf_err_t ret = MDF_OK;
    mlink_trigger_t *trigger_list = g_trigger_list;
    mlink_trigger_t *trigger_item = NULL;

    trigger_item = mlink_trigger_parse(trigger_raw_data);
    MDF_ERROR_CHECK(!trigger_item, MDF_FAIL, "mlink_trigger_parse");

    for (mlink_trigger_t *trigger_idex = trigger_list->next; trigger_idex;
            trigger_list = trigger_idex, trigger_idex = trigger_idex->next) {
        if (!strcasecmp(trigger_idex->name, trigger_item->name)) {
            MDF_LOGD("remove event: %s", trigger_idex->name);
            ret = mdf_info_erase(trigger_idex->name);
            MDF_ERROR_CONTINUE(ret != MDF_OK, "mdf_info_erase, ret: %d", ret);

            trigger_list->next = trigger_idex->next;
			trigger_free(&trigger_idex);
			// MDF_FREE(trigger_idex->alias);
            // MDF_FREE(trigger_idex->addrs_list);
			// for(int i=0;i<trigger_idex->content_num;i++){
			// 	MDF_FREE(trigger_idex->execute_content[i]);
			// }
			// MDF_FREE(trigger_idex->execute_content);
			// for(int i=0;i<trigger_idex->condition_num;i++){
			// 	MDF_FREE(trigger_idex->condition_list[i]);
			// }
			// MDF_FREE(trigger_idex->condition_list);
            // MDF_FREE(trigger_idex);
            trigger_idex = trigger_list;
            break;
        }
    }

    trigger_item->next   = g_trigger_list->next;
    g_trigger_list->next = trigger_item;

    ret = mdf_info_save(trigger_item->name, trigger_raw_data, strlen(trigger_raw_data) + 1);
    MDF_ERROR_CHECK(ret < 0, ret, "Save the information");

    ret = mlink_trigger_store_update();
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "mlink_trigger_store_update");

    return MDF_OK;
}

static mdf_err_t mlink_handle_set_trigger(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret   = MDF_OK;
    int trigger_num = 0;
    char *trigger_raw_data[MLINK_TRIGGER_LIST_MAX_NUM] = {NULL};

    ret = mlink_json_parse(handle_data->req_data, "events", &trigger_num);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

    ret = mlink_json_parse(handle_data->req_data, "events", trigger_raw_data);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

    for (int i = 0; i < trigger_num; ++i) {
        ret = mlink_trigger_add(trigger_raw_data[i]);
		MDF_FREE(trigger_raw_data[i]);
        MDF_ERROR_CHECK(ret != MDF_OK, ret, "mlink_trigger_add");

        MDF_LOGD("mlink_handle_set_trigger success");
    }

    mdf_event_loop_send(MDF_EVENT_MLINK_SET_TRIGGER, NULL);

    return MDF_OK;
}

static mdf_err_t mlink_handle_remove_trigger(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret         = ESP_OK;
    char trigger_name[16] = {0};
    int trigger_num       = 0;
    char *trigger_list[MLINK_TRIGGER_LIST_MAX_NUM] = {0};

    ret = mlink_json_parse(handle_data->req_data, "name", &trigger_num);
    MDF_ERROR_CHECK(ret < 0, ret, "Parse the json formatted string");

    ret = mlink_json_parse(handle_data->req_data, "name", trigger_list);
    MDF_ERROR_CHECK(ret < 0, ret, "Parse the json formatted string");

    for (int i = 0; i < trigger_num; ++i) {

        mlink_trigger_t *trigger_idex_prior = g_trigger_list;

        for (mlink_trigger_t *trigger_idex = trigger_idex_prior->next; trigger_idex;
                trigger_idex_prior = trigger_idex, trigger_idex = trigger_idex->next) {
            if (!strcasecmp(trigger_idex->name, trigger_list[i])) {
                MDF_LOGD("remove event: %s", trigger_idex->name);
                ret = mdf_info_erase(trigger_idex->name);
                MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "mdf_info_erase, ret: %d", ret);

                trigger_idex_prior->next = trigger_idex->next;
				trigger_free(&trigger_idex);
                trigger_idex = trigger_idex_prior;
                break;
            }
        }
        MDF_FREE(trigger_list[i]);
    
	}

    ret = mlink_trigger_store_update();
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "mlink_trigger_store_update");

    return ESP_OK;
}

static mdf_err_t mlink_handle_remove_all_trigger(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret         = ESP_OK;

    mlink_trigger_t *trigger_idex_prior = g_trigger_list;

    for (mlink_trigger_t *trigger_idex = trigger_idex_prior->next; trigger_idex;
            trigger_idex_prior = trigger_idex, trigger_idex = trigger_idex->next) {

        MDF_LOGD("remove event: %s", trigger_idex->name);
        ret = mdf_info_erase(trigger_idex->name);
        MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "mdf_info_erase, ret: %d", ret);

        trigger_idex_prior->next = trigger_idex->next;
		trigger_free(&trigger_idex);
		// MDF_FREE(trigger_idex->alias);
        // MDF_FREE(trigger_idex->addrs_list);
		// for(int i=0;i<trigger_idex->content_num;i++){
		// 	MDF_FREE(trigger_idex->execute_content[i]);
		// }
		// MDF_FREE(trigger_idex->execute_content);
		// for(int i=0;i<trigger_idex->condition_num;i++){
		// 	MDF_FREE(trigger_idex->condition_list[i]);
		// }
		// MDF_FREE(trigger_idex->condition_list);
        // MDF_FREE(trigger_idex);
        trigger_idex = trigger_idex_prior;
    }

    ret = mlink_trigger_store_update();
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "mlink_trigger_store_update");

    return ESP_OK;
}


static mdf_err_t mlink_handle_trigger_trigger(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret   = MDF_OK;
    int trigger_num       = 0;
    char *trigger_list[MLINK_TRIGGER_LIST_MAX_NUM] = {0};

    ret = mlink_json_parse(handle_data->req_data, "name", &trigger_num);
    MDF_ERROR_CHECK(ret < 0, ret, "Parse the json formatted string");

    ret = mlink_json_parse(handle_data->req_data, "name", trigger_list);
    MDF_ERROR_CHECK(ret < 0, ret, "Parse the json formatted string");

    for (int i = 0; i < trigger_num; ++i) {
		
		for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {
			if(strcasecmp(trigger_list[i], trigger_idex->name) == 0){
				execute_trigger_content(trigger_idex);
				break;
			}
		}
		MDF_FREE(trigger_list[i]);
    }

    return MDF_OK;
}

static mdf_err_t mlink_handle_set_trigger_enable(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret   = MDF_OK;
    char trigger_name[16] = {0};
    int trigger_num       = 0;
    char *trigger_list[MLINK_TRIGGER_LIST_MAX_NUM] = {0};

    ret = mlink_json_parse(handle_data->req_data, "events", &trigger_num);
    MDF_ERROR_CHECK(ret < 0, ret, "Parse the json formatted string");

    ret = mlink_json_parse(handle_data->req_data, "events", trigger_list);
    MDF_ERROR_CHECK(ret < 0, ret, "Parse the json formatted string");


    for (int i = 0; i < trigger_num; ++i) {
		
		bool enable;
		ret = mlink_json_parse(trigger_list[i], "name", trigger_name);
		MDF_ERROR_CONTINUE(ret < 0, "Parse the json formatted string");
		
		ret = mlink_json_parse(trigger_list[i], "enable", &enable);
		MDF_ERROR_CONTINUE(ret < 0, "Parse the json formatted string");	
		
		for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {
		
			if(strcasecmp(trigger_name, trigger_idex->name) != 0){
				continue;
			}
			char *raw_data = NULL;
			cJSON * trigger_json = NULL;
			cJSON * enable_json =NULL;
			
			raw_data = MDF_MALLOC(trigger_idex->raw_data_size);
			ret = __mdf_info_load(trigger_idex->name, raw_data, trigger_idex->raw_data_size, LENGTH_TYPE_NUMBER);
			if(ret != ESP_OK){
				goto BREAK;
			}
			
			trigger_json = cJSON_Parse(raw_data);
			if(NULL == trigger_json){
				goto BREAK;
			}
			
			enable_json = cJSON_GetObjectItem(trigger_json, "enable");
			if(enable_json){
				
				// 判断值是否相同，相同则退出
				if(cJSON_IsTrue(enable_json) == enable){
					goto BREAK;
				}
				enable_json->type = enable ? cJSON_True : cJSON_False;
				
			} else{
				cJSON_AddBoolToObject(trigger_json, "enable", enable);
			}
			
			char *new_raw_data = cJSON_PrintUnformatted(trigger_json);
			if(new_raw_data){
				trigger_idex->raw_data_size = strlen(new_raw_data) + 1;
				trigger_idex->enable = enable;
				mdf_info_save(trigger_idex->name, new_raw_data, trigger_idex->raw_data_size);
				MDF_FREE(new_raw_data);
				mlink_trigger_store_update();
			}
BREAK:
			if(trigger_json){
				cJSON_Delete(trigger_json);
			}
			MDF_FREE(raw_data);
			break;
		}

    }
	
	// 释放资源
    for (int i = 0; i < trigger_num; ++i) {
		MDF_FREE(trigger_list[i]);
	}

    return MDF_OK;
}

/*
 * read cid value as initial value of trigger compare
 */
static mdf_err_t mlink_trigger_read_cid_value(void)
{
	mdf_err_t ret = ESP_OK;
    int value     = -1;

    for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {
		/* do not scan for trigger of type is TRIGGER_TIMING  */
		if(trigger_idex->trigger_type == TRIGGER_TIMING || trigger_idex->trigger_type == TRIGGER_LOOP
		|| trigger_idex->trigger_type == TRIGGER_AND
		|| trigger_idex->trigger_type ==TRIGGER_OR)
			continue;

		if(strlen(trigger_idex->node) == 0) {
			/* cid have perms trigger? */
			if((CHARACTERISTIC_PERMS_TRIGGER & mlink_get_characteristics_perms(trigger_idex->trigger_cid)) != CHARACTERISTIC_PERMS_TRIGGER){
				MDF_LOGD("cid: %s not perms", trigger_idex->trigger_cid);
				continue;
			}
			ret = mlink_device_get_value(trigger_idex->trigger_cid, &value);
			
			trigger_compare_t *cmp = &trigger_idex->trigger_compare;
			cmp->value = value;
		}else{
			uint8_t index = atoi(&(trigger_idex->node[1]));
			subdev_port_t *subdev = mlink_get_subdev_handle_by_index(index);
			if(subdev == NULL){
				continue;
			}
			subdev_cid_t *subdev_cid = mlink_get_subdev_cid_handle_by_name(subdev, trigger_idex->trigger_cid, NULL);
			MDF_ERROR_CONTINUE(subdev_cid == NULL, "<%s> subdev_find_addr, cid: %s", mdf_err_to_name(MDF_ERR_NOT_FOUND), trigger_idex->trigger_cid);

			trigger_compare_t *cmp = &trigger_idex->trigger_compare;
			cmp->value = value;			
			if(strcmp(subdev_cid->type, "u16") == 0){
				cmp->value = subdev_cid->value.value_u16;
			} else if(strcmp(subdev_cid->type, "u32") == 0){
				cmp->value = subdev_cid->value.value_u32;
			} else if(strcmp(subdev_cid->type, "i16") == 0){
				cmp->value = subdev_cid->value.value_i16;
			} else if(strcmp(subdev_cid->type, "i32") == 0){
				cmp->value = subdev_cid->value.value_i32;
			} else if(strcmp(subdev_cid->type, "float") == 0){
				cmp->value = subdev_cid->value.value_float;
			}
		}
    }
	return MDF_OK;
}

// 判断触发器的条件是否成立
static int is_trigger_condition_true(mlink_trigger_t *trigger_idex) {
    mdf_err_t ret   = MDF_OK;
	float value;
    characteristic_value_t value_union = {.value_int = -1};

	time_t now;
	struct tm timeinfo;
	time(&now);
	localtime_r(&now, &timeinfo);
	uint32_t time_of_day = timeinfo.tm_hour * 3600 + timeinfo.tm_min * 60 + timeinfo.tm_sec;

	/* trigger is enable  */
	if(trigger_idex->enable == false)
		return ESP_FAIL;

	/* valid time compare  */
	trigger_valid_time_t *valid_time = &trigger_idex->valid_time;
	if(valid_time->end_date > 0 && valid_time->week != 0){
		if( !((valid_time->week & (1 << timeinfo.tm_wday)) != 0) ){
			return ESP_FAIL;
		}
		if( !(now >= valid_time->start_date && now <= valid_time->end_date) ){
			return ESP_FAIL;
		}
		if(valid_time->start_time != 0 && valid_time->end_time != 0){
			if( !(time_of_day >= valid_time->start_time && time_of_day <= valid_time->end_time) ) {
				return ESP_FAIL;
			}
		}
	}

	// 判断时间条件是否成立
	if(trigger_idex->trigger_type == TRIGGER_TIMING) {
		if( (timeinfo.tm_hour == trigger_idex->trigger_params[0] && timeinfo.tm_min == trigger_idex->trigger_params[1]) ) {
			return ESP_OK;
		} else {
			return ESP_FAIL;
		}
	} else if(trigger_idex->trigger_type == TRIGGER_LOOP) {
		if((now - trigger_idex->last_trigger_time > (trigger_idex->trigger_params[0]*3600+trigger_idex->trigger_params[1]*60))){
			trigger_idex->last_trigger_time = now;
			return ESP_OK;
		} else {
			return ESP_FAIL;
		}
	}
	// 判断多条件是否成立
	else if(trigger_idex->trigger_type == TRIGGER_AND || trigger_idex->trigger_type == TRIGGER_OR) {
		return mlink_trigger_multi_condition_handle(trigger_idex);
	}

	// 判断其它类型的条件
	/* cid have perms trigger? */
	if((CHARACTERISTIC_PERMS_TRIGGER & mlink_get_characteristics_perms(trigger_idex->trigger_cid)) != CHARACTERISTIC_PERMS_TRIGGER){
		MDF_LOGD("cid: %s not perms", trigger_idex->trigger_cid);
		return ESP_FAIL;
	}
	characteristic_format_t format = mlink_get_characteristics_format(trigger_idex->trigger_cid);
	/* value compare */
	trigger_compare_t *cmp = &trigger_idex->trigger_compare;

	if(strlen(trigger_idex->node) == 0) {
		ret = mlink_device_get_value(trigger_idex->trigger_cid, &value_union);
		MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "<%s> mlink_device_get_value, cid: %s", mdf_err_to_name(ret), trigger_idex->trigger_cid);
		if(format == CHARACTERISTIC_FORMAT_DOUBLE){
			value = value_union.value_float;
		}else{
			value = value_union.value_int;
		}
	}else{
		uint8_t index = atoi(&(trigger_idex->node[1]));
		subdev_port_t *subdev = mlink_get_subdev_handle_by_index(index);
		if(subdev == NULL){
			return ESP_FAIL;
		}
		subdev_cid_t *subdev_cid = mlink_get_subdev_cid_handle_by_name(subdev, trigger_idex->trigger_cid, NULL);
		MDF_ERROR_CHECK(subdev_cid == NULL, ESP_FAIL, "<%s> subdev_find_addr, cid: %s", mdf_err_to_name(MDF_ERR_NOT_FOUND), trigger_idex->trigger_cid);

		if(strcmp(subdev_cid->type, "u16") == 0){
			value = subdev_cid->value.value_u16;
		} else if(strcmp(subdev_cid->type, "u32") == 0){
			value = subdev_cid->value.value_u32;
		} else if(strcmp(subdev_cid->type, "i16") == 0){
			value = subdev_cid->value.value_i16;
		} else if(strcmp(subdev_cid->type, "i32") == 0){
			value = subdev_cid->value.value_i32;
		} else {
			value = subdev_cid->value.value_float;
		}
	}
	
	bool equal		  = (!cmp->flag.equal		 || (cmp->flag.equal		&& cmp->equal == value)) ? true 	  : false;
	bool unequal	  = (!cmp->flag.unequal 	 || (cmp->flag.unequal		&& cmp->unequal != value)) ? true	  : false;
	bool greater_than = (!cmp->flag.greater_than || (cmp->flag.greater_than && cmp->greater_than < value)) ? true : false;
	bool less_than	  = (!cmp->flag.less_than	 || (cmp->flag.less_than	&& cmp->less_than > value)) ? true	  : false;
	bool variation	  = (!cmp->flag.variation	 || (cmp->flag.variation	&& cmp->variation <= fabsf(value - cmp->value))) ? true : false;
	bool rising 	  = (!cmp->flag.rising		 || (cmp->flag.rising		&& ((value - cmp->value) >= cmp->rising))) ? true : false;
	bool falling	  = (!cmp->flag.falling 	 || (cmp->flag.falling		&& ((cmp->value - value) >= cmp->falling))) ? true : false;
	// bool rising       = (!cmp->flag.rising       || (cmp->flag.rising       && (value - cmp->value >= variation))) ? true : false;
	// bool falling      = (!cmp->flag.falling      || (cmp->flag.falling      && (cmp->value - value >= variation))) ? true : false;

	MDF_LOGI("name: %s, node: %s, cid: %s, value: %f, trigger_type: %d, variation: %d, trigger: %f, rising: %f",
				trigger_idex->name, trigger_idex->node, trigger_idex->trigger_cid, value,
				trigger_idex->trigger_type, variation, cmp->value, cmp->rising);

	if (!equal || !unequal || !greater_than || !less_than || !variation || !rising || !falling) {
		cmp->value = value;
		return ESP_FAIL;
	}

	if (trigger_idex->trigger_type == TRIGGER_SYNC) {
		if (cmp->value == value) {
			return ESP_FAIL;
		}
	}
	
	cmp->value = value;

    return MDF_OK;
}

// 执行触发器的动作
static mdf_err_t execute_trigger_content(mlink_trigger_t *trigger_idex) {
	int ret = ESP_OK;

	characteristic_value_t value_union = {.value_int = -1};
	if (trigger_idex->trigger_type == TRIGGER_SYNC) {
		char *execute_content = NULL;
		if(strlen(trigger_idex->node) > 0){
			uint8_t index = atoi(&(trigger_idex->node[1]));
			subdev_port_t *subdev = mlink_get_subdev_handle_by_index(index);
			if(subdev == NULL){
				return ESP_FAIL;
			}
			subdev_cid_t *subdev_cid = mlink_get_subdev_cid_handle_by_name(subdev, trigger_idex->trigger_cid, NULL);
			MDF_ERROR_CHECK(subdev_cid == NULL, ESP_FAIL, "<%s> subdev_find_addr, cid: %s", mdf_err_to_name(MDF_ERR_NOT_FOUND), trigger_idex->trigger_cid);

			if(strcmp(subdev_cid->type, "float") == 0) {
				asprintf(&execute_content, "{\"request\":\"subdev_set_data\",\"node\":\"%s\",\"cids\":[{\"cid\":\"%s\",\"value\":%f}]}",
						trigger_idex->node, (char *)trigger_idex->trigger_params, subdev_cid->value.value_float);
			}else{
				asprintf(&execute_content, "{\"request\":\"subdev_set_data\",\"node\":\"%s\",\"cids\":[{\"cid\":\"%s\",\"value\":%d}]}",
						trigger_idex->node, (char *)trigger_idex->trigger_params, (int)subdev_cid->value.value_i32);
			}
		}else {
			characteristic_format_t format = mlink_get_characteristics_format(trigger_idex->trigger_cid);
			if(format == CHARACTERISTIC_FORMAT_DOUBLE){
				asprintf(&execute_content, "{\"request\":\"set_status\",\"characteristics\":[{\"cid\":\"%s\",\"value\":%f}]}",
						(char *)trigger_idex->trigger_params, (float)value_union.value_float);
			}else{
				asprintf(&execute_content, "{\"request\":\"set_status\",\"characteristics\":[{\"cid\":\"%s\",\"value\":%d}]}",
						(char *)trigger_idex->trigger_params, value_union.value_int);
			}
		}
		ESP_LOGI(TAG, "exec sync trigger content, name: %s", trigger_idex->name);

		mlink_handle_data_t *handle_data   = MDF_CALLOC(1, sizeof(mlink_handle_data_t));
		assert(handle_data);
		handle_data->req_data = execute_content;
		handle_data->req_size	  = strlen(execute_content);
		handle_data->req_fromat  = MLINK_HTTPD_FORMAT_JSON;
		handle_data->resp_fromat = MLINK_HTTPD_FORMAT_JSON;
		xQueueSend(g_mlink_handle_queue, &handle_data, portMAX_DELAY);

		mdf_event_loop_send(MDF_EVENT_MLINK_TRIGGER_TRIGGER, trigger_idex);
		return MDF_OK;
	}

	ESP_LOGI(TAG, "exec trigger content, name: %s", trigger_idex->name);

	for (size_t i = 0; i < trigger_idex->content_num; i++) {
		mlink_handle_data_t *handle_data   = MDF_CALLOC(1, sizeof(mlink_handle_data_t));
		assert(handle_data);
		asprintf(&handle_data->req_data, "%s",trigger_idex->execute_content[i]);
		handle_data->req_size	  = strlen(trigger_idex->execute_content[i]);
		handle_data->req_fromat  = MLINK_HTTPD_FORMAT_JSON;
		handle_data->resp_fromat = MLINK_HTTPD_FORMAT_JSON;
		xQueueSend(g_mlink_handle_queue, &handle_data, portMAX_DELAY);

	}
	
	mdf_event_loop_send(MDF_EVENT_MLINK_TRIGGER_TRIGGER, trigger_idex);

    return MDF_OK;
}

// 根据触发器名称，在g_trigger_list中找到对应的触发器，并返回内容
static mlink_trigger_t *mlink_trigger_get_by_name(const char *name) {

    for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {
        if (strcmp(trigger_idex->name, name) == 0) {
            return trigger_idex;
        }
    }
    return NULL;
}
 
// 触发器多条件判断
// return 0 条件成立，!=0 条件不成立
static mdf_err_t mlink_trigger_multi_condition_handle(mlink_trigger_t *trigger_idex) {

	// int ret = ESP_OK;
	bool is_error = false; // 条件执行结果

	if(trigger_idex->trigger_type != TRIGGER_AND &&  trigger_idex->trigger_type != TRIGGER_OR) {
		return MDF_FAIL;
	}
	ESP_LOGI(TAG, "trigger_name: %s, condition_num: %d, type: %d", trigger_idex->name, trigger_idex->condition_num, trigger_idex->trigger_type);
	for(int i=0; i<trigger_idex->condition_num; i++){
		assert(trigger_idex->condition_list!= NULL);
		assert(trigger_idex->condition_list[i]!= NULL);

		mlink_trigger_t *t = mlink_trigger_get_by_name(trigger_idex->condition_list[i]);
		if(t == NULL){
			MDF_LOGW("trigger name: %s not found", trigger_idex->condition_list[i]);
			is_error = true;
			break;
		}
		is_error = is_trigger_condition_true(t);
		ESP_LOGI(TAG, "condition result %d", is_error);
		if(trigger_idex->trigger_type == TRIGGER_AND ) {
			if(is_error == true){
				break;
			}
		}
		else if(trigger_idex->trigger_type == TRIGGER_OR){
			if(is_error == false){
				break;
			}				
		}

	}
	// 条件不成立
	if(is_error == true){
		return MDF_FAIL;
	}

	return MDF_OK;	
}

static mdf_err_t trigger_run(mlink_trigger_t *trigger_idex) {
	int ret = ESP_FAIL;
	if(trigger_idex->trigger_type == TRIGGER_AND){
		ret = mlink_trigger_multi_condition_handle(trigger_idex);
		if (ESP_OK == ret) {
			// 条件成立，执行动作
			execute_trigger_content(trigger_idex);
		}
	}
	else if(trigger_idex->trigger_type == TRIGGER_OR){
		execute_trigger_content(trigger_idex);
	}
	else{
		ret = is_trigger_condition_true(trigger_idex);
		if (ESP_OK == ret) {
			execute_trigger_content(trigger_idex);
		}
	}
	return ret;
}

static mdf_err_t mlink_handle_execute_trigger(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret   = MDF_OK;
	int trigger_num = 0;
    char **trigger_name = NULL;

    ret = mlink_json_parse(handle_data->req_data, "name", &trigger_num);
    MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "mdf_info_save, ret: %d", ret);
	if(trigger_num <= 0){
		MDF_LOGW("trigger_num <= 0");
		return ESP_FAIL;
	}
    trigger_name = MDF_CALLOC(trigger_num, sizeof(char *));
	assert(trigger_name);
    ret = mlink_json_parse(handle_data->req_data, "name", trigger_name);
    MDF_ERROR_GOTO(ret < 0, EXIT, "Parse the json formatted string");

	for(int i=0; i<trigger_num; i++) {
		ESP_LOGI(TAG, "execute trigger, name: %s", trigger_name[i]);
		for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {
			if(strcmp(trigger_idex->name, trigger_name[i]) == 0) {
				ret = trigger_run(trigger_idex);
			}
		}
		MDF_FREE(trigger_name[i]);
	}
EXIT:
	MDF_FREE(trigger_name);
	return ret;
}

mdf_err_t mlink_trigger_handle(mlink_communicate_t communicate)
{
    // mdf_err_t ret = ESP_OK;
	// int value = -1;
    // characteristic_value_t value_union = {.value_int = -1};
	// uint8_t self_mac[6] 		 = {0};
	
	// esp_wifi_get_mac(ESP_IF_WIFI_STA, self_mac);

    MDF_ERROR_CHECK(!g_trigger_list, MDF_ERR_NOT_INIT, "mlink_trigger is not initialized");

	// time_t now;
	// struct tm timeinfo;
	// time(&now);
	// localtime_r(&now, &timeinfo);
	// uint32_t time_of_day = timeinfo.tm_hour * 3600 + timeinfo.tm_min * 60 + timeinfo.tm_sec;

    for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {

		/* do not scan for trigger of type is TRIGGER_TIMING  */
		if(trigger_idex->trigger_type != TRIGGER_LINKAGE && trigger_idex->trigger_type != TRIGGER_SYNC)
			continue;
		if(strlen(trigger_idex->parent) != 0)
			continue;
		trigger_run(trigger_idex);

		// /* trigger is enable  */
		// if(trigger_idex->enable == false)
		// 	continue;

		// /* valid time compare  */
		// trigger_valid_time_t *valid_time = &trigger_idex->valid_time;
		// if(valid_time->end_date > 0 && valid_time->week != 0){
		// 	if( !((valid_time->week & (1 << timeinfo.tm_wday)) != 0) ){
		// 		continue;
		// 	}
		// 	if( !(now >= valid_time->start_date && now <= valid_time->end_date) ){
		// 		continue;
		// 	}
		// 	if(valid_time->start_time != 0 && valid_time->end_time != 0){
		// 		if( !(time_of_day >= valid_time->start_time && time_of_day <= valid_time->end_time) ){
		// 			continue;
		// 		}
		// 	}
		// }

		// /* cid have perms trigger? */
		// if((CHARACTERISTIC_PERMS_TRIGGER & mlink_get_characteristics_perms(trigger_idex->trigger_cid)) != CHARACTERISTIC_PERMS_TRIGGER){
		// 	MDF_LOGD("cid: %s not perms", trigger_idex->trigger_cid);
		// 	continue;
		// }
		// characteristic_format_t format = mlink_get_characteristics_format(trigger_idex->trigger_cid);
		// /* value compare */
		// trigger_compare_t *cmp = &trigger_idex->trigger_compare;
		// subdev_cid_t subdev_cid;
		// if(strlen(trigger_idex->node) == 0) {
		// 	ret = mlink_device_get_value(trigger_idex->trigger_cid, &value_union);
		// 	MDF_ERROR_CONTINUE(ret < 0, "<%s> mlink_device_get_value, cid: %s", mdf_err_to_name(ret), trigger_idex->trigger_cid);
		// 	if(format == CHARACTERISTIC_FORMAT_DOUBLE){
		// 		value = value_union.value_float;
		// 	}else{
		// 		value = value_union.value_int;
		// 	}
		// }else{
		// 	uint8_t ch;
		
		// 	if(strcmp(trigger_idex->node, "s0") == 0){
		// 		ch = 0;
		// 	} else if(strcmp(trigger_idex->node, "s1") == 0) {
		// 		ch = 1;
		// 	}else {
		// 		ret = ESP_FAIL;
		// 		MDF_ERROR_CONTINUE(ret < 0, "<%s> invalid node, cid: %s", mdf_err_to_name(ret), trigger_idex->trigger_cid);
		// 	}
			
		// 	ret = subdev_find_cid(trigger_idex->trigger_cid, ch, &subdev_cid , NULL);
		// 	MDF_ERROR_CONTINUE(ret < 0, "<%s> subdev_find_addr, cid: %s", mdf_err_to_name(ret), trigger_idex->trigger_cid);

		// 	if(strcmp(subdev_cid.type, "u16") == 0){
		// 		value = subdev_cid.value.value_u16;
		// 	} else if(strcmp(subdev_cid.type, "u32") == 0){
		// 		value = subdev_cid.value.value_u32;
		// 	} else if(strcmp(subdev_cid.type, "i16") == 0){
		// 		value = subdev_cid.value.value_i16;
		// 	} else if(strcmp(subdev_cid.type, "i32") == 0){
		// 		value = subdev_cid.value.value_i32;
		// 	} else {
		// 		value = subdev_cid.value.value_float;
		// 	}
		// }
		
		// bool equal		  = (!cmp->flag.equal		 || (cmp->flag.equal		&& cmp->equal == value)) ? true 	  : false;
		// bool unequal	  = (!cmp->flag.unequal 	 || (cmp->flag.unequal		&& cmp->unequal != value)) ? true	  : false;
		// bool greater_than = (!cmp->flag.greater_than || (cmp->flag.greater_than && cmp->greater_than < value)) ? true : false;
		// bool less_than	  = (!cmp->flag.less_than	 || (cmp->flag.less_than	&& cmp->less_than > value)) ? true	  : false;
		// bool variation	  = (!cmp->flag.variation	 || (cmp->flag.variation	&& cmp->variation <= abs(value - cmp->value))) ? true : false;
		// bool rising 	  = (!cmp->flag.rising		 || (cmp->flag.rising		&& (cmp->value < cmp->rising && value >= cmp->rising))) ? true : false;
		// bool falling	  = (!cmp->flag.falling 	 || (cmp->flag.falling		&& (cmp->value > cmp->falling && value <= cmp->falling))) ? true : false;
        // // bool rising       = (!cmp->flag.rising       || (cmp->flag.rising       && (value - cmp->value >= variation))) ? true : false;
        // // bool falling      = (!cmp->flag.falling      || (cmp->flag.falling      && (cmp->value - value >= variation))) ? true : false;

		// MDF_LOGI("name: %s, node: %s, cid: %s, type %s, value: %d, trigger_type: %d, variation: %d, trigger: %d, rising: %d",
		// 		 trigger_idex->name, trigger_idex->node, trigger_idex->trigger_cid, subdev_cid.type, value,
		// 		 trigger_idex->trigger_type, variation, cmp->value, cmp->rising);
		
		// if (!equal || !unequal || !greater_than || !less_than || !variation || !rising || !falling) {
		// 	continue;
		// }

		// if (trigger_idex->trigger_type == TRIGGER_SYNC) {
		// 	if (cmp->value == value) {
		// 		continue;
		// 	}
		// 	if(strlen(trigger_idex->node) > 0){
		// 		if(strcmp(subdev_cid.type, "float") == 0) {
		// 			asprintf(&trigger_idex->execute_content, "{\"request\":\"subdev_set_data\",\"node\":\"%s\",\"cids\":[{\"cid\":\"%s\",\"value\":%f}]}",
		// 					trigger_idex->node, (char *)trigger_idex->trigger_params, subdev_cid.value.value_float);
		// 		}else{
		// 			asprintf(&trigger_idex->execute_content, "{\"request\":\"subdev_set_data\",\"node\":\"%s\",\"cids\":[{\"cid\":\"%s\",\"value\":%d}]}",
		// 					trigger_idex->node, (char *)trigger_idex->trigger_params, subdev_cid.value.value_i32);
		// 		}
		// 	}else {
		// 		if(format == CHARACTERISTIC_FORMAT_DOUBLE){
		// 			asprintf(&trigger_idex->execute_content, "{\"request\":\"set_status\",\"characteristics\":[{\"cid\":\"%s\",\"value\":%f}]}",
		// 					(char *)trigger_idex->trigger_params, (float)value_union.value_float);
		// 		}else{
		// 			asprintf(&trigger_idex->execute_content, "{\"request\":\"set_status\",\"characteristics\":[{\"cid\":\"%s\",\"value\":%d}]}",
		// 					(char *)trigger_idex->trigger_params, value_union.value_int);
		// 		}
		// 	}
		// }
		
		// cmp->value = value;
		
		// for (int i = 0; i < trigger_idex->addrs_num; ++i) {
		// 	bool need_handle_data = false;
		// 	mesh_addr_t dest_addr;
		// 	memcpy(dest_addr.addr, trigger_idex->addrs_list + i*6, MWIFI_ADDR_LEN);
		
		// 	if (MWIFI_ADDR_IS_EMPTY(dest_addr.addr)
		// 		|| MWIFI_ADDR_IS_ANY(dest_addr.addr)
		// 		//|| MWIFI_ADDR_IS_BROADCAST(dest_addr.addr)
		// 		|| !memcmp(dest_addr.addr, self_mac, MWIFI_ADDR_LEN)) {
		// 		need_handle_data = true;
		// 	}

		// 	if(need_handle_data){
		// 		// mlink_handle_data_t handle_data = {
		// 		// 	.req_data	  = (char *)trigger_idex->execute_content,
		// 		// 	.req_size	  = strlen(trigger_idex->execute_content),
		// 		// 	.req_fromat  = MLINK_HTTPD_FORMAT_JSON,
		// 		// 	.resp_data   = NULL,
		// 		// 	.resp_size   = 0,
		// 		// 	.resp_fromat = MLINK_HTTPD_FORMAT_JSON,
		// 		// };
		// 		// ret = mlink_handle_request(&handle_data);
		// 		// MDF_FREE(handle_data.resp_data);

		// 		mlink_handle_data_t *handle_data   = MDF_CALLOC(1, sizeof(mlink_handle_data_t));
		// 		asprintf(&handle_data->req_data, "%s", trigger_idex->execute_content);
		// 		handle_data->req_size	  = strlen(trigger_idex->execute_content);
		// 		handle_data->req_fromat  = MLINK_HTTPD_FORMAT_JSON;
		// 		handle_data->resp_fromat = MLINK_HTTPD_FORMAT_JSON;
		// 		xQueueSend(g_mlink_handle_queue, &handle_data, portMAX_DELAY);
		// 	}
		// }

        // MDF_LOGD("addrs_num: %d, addrs_list: " MACSTR ", execute_content: %s",
        //          trigger_idex->addrs_num, MAC2STR(trigger_idex->addrs_list),
        //          trigger_idex->execute_content);

        // if (trigger_idex->trigger_type == TRIGGER_SYNC) {
        //     MDF_FREE(trigger_idex->execute_content);
        // }

		// mdf_event_loop_send(MDF_EVENT_MLINK_TRIGGER_TRIGGER, trigger_idex);

        // MDF_ERROR_CONTINUE(ret != ESP_OK, "execute_content");
    }

    return MDF_OK;
}

mdf_err_t mlink_trigger_timming_handle(mlink_communicate_t communicate)
{
    // mdf_err_t ret = ESP_OK;
	// uint8_t self_mac[6] 		 = {0};
	// esp_wifi_get_mac(ESP_IF_WIFI_STA, self_mac);

    MDF_ERROR_CHECK(!g_trigger_list, MDF_ERR_NOT_INIT, "mlink_trigger is not initialized");

	time_t now;
	struct tm timeinfo;
	time(&now);
	localtime_r(&now, &timeinfo);
	if(now < 1577808000){
		// 时间未校准 退出
		return MDF_FAIL;
	}
    for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {

		/* do not scan for trigger of type is not TRIGGER_TIMING  */
		if(trigger_idex->trigger_type != TRIGGER_TIMING && trigger_idex->trigger_type != TRIGGER_LOOP)
			continue;
		
		trigger_run(trigger_idex);

		// /* trigger is enable  */
		// if(trigger_idex->enable == false)
		// 	continue;

		// /* valid time compare  */
		// trigger_valid_time_t *valid_time = &trigger_idex->valid_time;
		// if(valid_time->end_date > 0 && valid_time->week != 0){

		// 	uint32_t time_of_day = timeinfo.tm_hour * 3600 + timeinfo.tm_min * 60 + timeinfo.tm_sec;
		// 	if( !((valid_time->week & (1 << timeinfo.tm_wday)) != 0) )
		// 		continue;
		// 	if( !(now >= valid_time->start_date && now <= valid_time->end_date) )
		// 		continue;
		// 	if(valid_time->start_time != 0 && valid_time->end_time != 0){
		// 		if( !(time_of_day >= valid_time->start_time && time_of_day <= valid_time->end_time) )
		// 			continue;
		// 	}
		// }

		// /* time compare */
		// MDF_LOGD("target time %d:%d", trigger_idex->trigger_params[0], trigger_idex->trigger_params[1]);
		// if(trigger_idex->trigger_type == TRIGGER_TIMING){
		// 	if( !(timeinfo.tm_hour == trigger_idex->trigger_params[0] && timeinfo.tm_min == trigger_idex->trigger_params[1]) )
		// 		continue;
		// } else if(trigger_idex->trigger_type == TRIGGER_LOOP) {
		// 	if(!(now - trigger_idex->last_trigger_time > (trigger_idex->trigger_params[0]*3600+trigger_idex->trigger_params[1]*60)))
		// 		continue;
		// 	trigger_idex->last_trigger_time = now;
		// }

		// /* compare success, send request */
		// // 如果是自身mac则不发送espnow
		// // if(!(trigger_idex->addrs_num == 1 && 0 == memcmp(trigger_idex->addrs_list, self_mac, MWIFI_ADDR_LEN))){
	    // //     ret = mlink_espnow_write(trigger_idex->addrs_list, trigger_idex->addrs_num, trigger_idex->execute_content,
	    // //                              strlen(trigger_idex->execute_content), trigger_idex->communicate_type, portMAX_DELAY);
		// // }
		
		// for (int i = 0; i < trigger_idex->addrs_num; ++i) {
		// 	bool handle_data = false;
		// 	mesh_addr_t dest_addr;
		// 	memcpy(dest_addr.addr, trigger_idex->addrs_list + i*6, MWIFI_ADDR_LEN);
		
		// 	if (MWIFI_ADDR_IS_EMPTY(dest_addr.addr)
		// 		|| MWIFI_ADDR_IS_ANY(dest_addr.addr)
		// 		//|| MWIFI_ADDR_IS_BROADCAST(dest_addr.addr)
		// 		|| !memcmp(dest_addr.addr, self_mac, MWIFI_ADDR_LEN)) {
		// 		handle_data = true;
		// 	}

		// 	if(handle_data){
		// 		mlink_handle_data_t handle_data = {
		// 			.req_data	 = (char *)trigger_idex->execute_content,
		// 			.req_size	 = strlen(trigger_idex->execute_content),
		// 			.req_fromat  = MLINK_HTTPD_FORMAT_JSON,
		// 			.resp_data	 = NULL,
		// 			.resp_size	 = 0,
		// 			.resp_fromat = MLINK_HTTPD_FORMAT_JSON,
		// 		};
		// 		ret = mlink_handle_request(&handle_data);
		// 		MDF_FREE(handle_data.resp_data);
		// 	}
		// }

        // MDF_LOGD("addrs_num: %d, addrs_list: " MACSTR ", execute_content: %s",
        //          trigger_idex->addrs_num, MAC2STR(trigger_idex->addrs_list),
        //          trigger_idex->execute_content);

        // if (trigger_idex->trigger_type == TRIGGER_SYNC) {
        //     MDF_FREE(trigger_idex->execute_content);
        // }

		// mdf_event_loop_send(MDF_EVENT_MLINK_TRIGGER_TRIGGER, trigger_idex);

        // MDF_ERROR_CONTINUE(ret != ESP_OK, "execute_content");
    }

    return MDF_OK;
}

mdf_err_t mlink_trigger_init()
{
    if (g_trigger_list) {
        return MDF_OK;
    }

    mdf_err_t ret                 = MDF_OK;
    g_trigger_list                = MDF_CALLOC(1, sizeof(mlink_trigger_t));
    char *trigger_raw_data        = NULL;
    mlink_trigger_t *trigger_item = NULL;
    mlink_trigger_store_t *trigger_store = MDF_CALLOC(MLINK_TRIGGER_LIST_MAX_NUM, sizeof(mlink_trigger_store_t));

    if (__mdf_info_load(MLINK_TRIGGER_STORE_KEY, trigger_store,
                      MLINK_TRIGGER_LIST_MAX_NUM * sizeof(mlink_trigger_store_t), LENGTH_TYPE_NUMBER) == MDF_OK) {
        for (int i = 0; i < MLINK_TRIGGER_LIST_MAX_NUM && trigger_store[i].size; ++i) {
            MDF_FREE(trigger_raw_data);
            trigger_raw_data = MDF_CALLOC(1, trigger_store[i].size);
            ret = __mdf_info_load(trigger_store[i].name, trigger_raw_data, trigger_store[i].size, LENGTH_TYPE_NUMBER);
            MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> Load the information", mdf_err_to_name(ret));

            trigger_item = mlink_trigger_parse(trigger_raw_data);
            MDF_ERROR_CONTINUE(!trigger_item, "<%s> mlink_trigger_parse", mdf_err_to_name(ret));

            trigger_item->next   = g_trigger_list->next;
            g_trigger_list->next = trigger_item;
        }
    }

    MDF_FREE(trigger_store);
    MDF_FREE(trigger_raw_data);

	mlink_trigger_read_cid_value();
	
    mlink_set_handle("set_event", mlink_handle_set_trigger);
    mlink_set_handle("get_event", mlink_handle_get_trigger);
    mlink_set_handle("remove_event", mlink_handle_remove_trigger);
	mlink_set_handle("trigger_event", mlink_handle_trigger_trigger);
	mlink_set_handle("remove_all_event", mlink_handle_remove_all_trigger);
	mlink_set_handle("set_event_en", mlink_handle_set_trigger_enable);
	mlink_set_handle("execute_event", mlink_handle_execute_trigger);

    return ESP_OK;
}

bool mlink_trigger_is_exist()
{
    return (!g_trigger_list || g_trigger_list->next == NULL) ? false : true;
}

int mlink_trigger_get_num()
{
	int count=0;
	if(!mlink_trigger_is_exist())
		return 0;
	
    for (mlink_trigger_t *trigger_idex = g_trigger_list->next; trigger_idex; trigger_idex = trigger_idex->next) {
		count++;
    }
	return count;
}

char *mlink_trigger_get_name(void *trigger)
{
	if(trigger)
		return ((mlink_trigger_t *)trigger)->name;
	return NULL;
}

char *mlink_trigger_get_alias(void *trigger)
{
	if(trigger)
		return ((mlink_trigger_t *)trigger)->alias;
	return NULL;
}

